package com.futu.openapi;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.*;
import com.sun.jna.*;


/*
update 09/15/2020
support x86 and arm platform
support os Linux(centos7)  Windows  Macos
author who.cat@qq.com
 */
//todo  guess the linux distro
class FTCAPIHelper {
    static final int WINDOWS = 1;
    static final int MAC = 2;
    static final int LINUX = 3;
    static final String LIBDIR = "lib";

    static int getOSType() {
        String osName = System.getProperty("os.name");
        if (osName.contains("Windows")) return WINDOWS;
        if (osName.contains("Linux")) return LINUX;
        if (osName.contains("Darwin") || osName.contains("Mac")) return MAC;
        return 0;
    }

    /*
     get os arc 32bit  or 64bit to adopt different native lib
     */

    static String getOSArch() {
        String osArch = System.getProperty("os.arch");
        if (osArch.contains("amd64")) {
            return "x86-64";
        }
        if (osArch.contains("ia64")) {
            return "x86-64";
        }
        if (osArch.contains("x64")) {
            return "x86";
        }
        // if no arch match return blank
        return "";
    }

    static String getLibName() {
        //todo the cur define incorrect must include the lib directory name "lib"
        //fix the lib path search problem
        String curDir = System.getProperty("user.dir") ;
        String libName = null;
        //todo must detect chipset family  x86 or x86-64 to match lib directory
        String libArchDir = getOSArch();

        int osType = getOSType();
        switch (osType) {
            case WINDOWS:
                libName = "FTAPIChannel.dll";
                libArchDir = "win32-" + libArchDir;
                break;
            case MAC:
                libName = "libFTAPIChannel.dylib";
                libArchDir = "darwin";
                break;
            case LINUX:
                libName = "libFTAPIChannel.so";
                libArchDir = "centos7-";
                break;
        }
        return Paths.get(curDir,LIBDIR,libArchDir,libName).toString();
    }

    static String extractLib() {
        String srcLibDir = null;
        Path libPath = null;
        String libName = null;
        String dstLibDir = System.getProperty("java.io.tmpdir");

        int osType = getOSType();
        switch (osType) {
            case WINDOWS:
                libName = "FTAPIChannel.dll";
                srcLibDir = "../../../lib/win32-x86/";
                break;
            case MAC:
                libName = "libFTAPIChannel.dylib";
                srcLibDir = "../../../lib/darwin/";
                break;
            case LINUX:
                libName = "libFTAPIChannel.so";
                srcLibDir = "../../../lib/linux-x86-64/";
                break;
        }

        extractFileFromJar(srcLibDir + libName, Paths.get(dstLibDir, libName).toString());
        return Paths.get(dstLibDir, libName).toString();
    }

    static void extractFileFromJar(String srcPath, String dstPath) {
        try (FileOutputStream writer = new FileOutputStream(dstPath);
                BufferedInputStream reader = new BufferedInputStream(FTCAPI.class.getResourceAsStream(srcPath))) {
            byte[] buffer = new byte[1024*1024];
            while (true) {
                int len = reader.read(buffer);
                if (len > 0)
                    writer.write(buffer, 0, len);
                else
                    break;
            }
        } catch (IOException e) {
            throw new LoadLibError(e.getMessage());
        }
    }
}


interface FTAPIChannel_OnDisConnectCallback extends Callback {
    void invoke(Pointer client, long nErrCode);
}

interface FTAPIChannel_OnInitConnectCallback extends Callback {
    void invoke(Pointer client, long errCode, String strDesc);
}

interface FTAPIChannel_OnReplyCallback extends Callback {
    void invoke(Pointer client, int enReqReplyType, ProtoHeader protoHeader, Pointer pProtoData, int nDataLen);
}

interface FTAPIChannel_OnPushCallback extends Callback {
    void invoke(Pointer client, ProtoHeader pProtoHeader, Pointer pProtoData, int nDataLen);
}

interface FTCAPI extends Library {
    FTCAPI INSTANCE = (FTCAPI)Native.load(FTCAPIHelper.getLibName(), FTCAPI.class);

    void FTAPIChannel_Init();
    Pointer CreateFTAPIChannel();
    void FTAPIChannel_UnInit();
    void ReleaseFTAPIChannel(Pointer client);
    void FTAPIChannel_SetClientInfo(Pointer client, String clientID, int clientVer);
    void FTAPIChannel_SetProgrammingLanguage(Pointer client, String progLang);
    void FTAPIChannel_SetRSAPrivateKey(Pointer client, String rsaPrivateKey);
    int FTAPIChannel_InitConnect(Pointer client, String ipAddr, short port, int enableEncrypt);
    long FTAPIChannel_GetConnectID(Pointer client);
    int FTAPIChannel_SendProto(Pointer client, int protoID, byte protoVer, byte[] data, int dataLen);
    int FTAPIChannel_Close(Pointer client);
    void FTAPIChannel_SetOnDisconnectCallback(Pointer client, FTAPIChannel_OnDisConnectCallback callback);
    void FTAPIChannel_SetOnInitConnectCallback(Pointer client, FTAPIChannel_OnInitConnectCallback callback);
    void FTAPIChannel_SetOnReplyCallback(Pointer client, FTAPIChannel_OnReplyCallback callback);
    void FTAPIChannel_SetOnPushCallback(Pointer client, FTAPIChannel_OnPushCallback callback);
}
